#! /usr/bin/env perl
##
## Copyright (C) by Argonne National Laboratory
##     See COPYRIGHT in top-level directory
##

# This file contains common routines for reading a file of function prototypes
# (such as mpi.h) and extracting the function prototypes.

#
# ReadInterface( filename, routineprefix, routinepattern, routinehash )
# Read file filename, look for routines that have a given prefix and name
# pattern, and insert that routine into routinehash with value the
# arguments of the routine.

#$Finalized_args = "bool";

sub ReadInterface {
    my $prototype_file = $_[0];
    my $routine_prefix = $_[1];
    my $routine_pattern = $_[2];
    my $routine_hash    = $_[3];
    # $debug is a global variable

    open( FD, "<$prototype_file" ) || die "Cannot open $prototype_file\n";

    # Skip to prototypes
    while (<FD>) {
        if ( /\/\*\s*Begin Prototypes/ ) { last; }
    }

    # Read each one
    while (<FD>) {
        # Handle the special case of prototypes to ignore
        if (/\/\*\s*Begin Skip Prototypes/) {
            while (<FD>) {
                if (/\/\*\s*End Skip Prototypes/) { last; }
            }
        }
        if (/\/\*\s*End Prototypes/) { last; }
        # Remove any comments
        $origline = $_;
        while (/(.*)\/\*(.*?)\*\/(.*)/) {
            my $removed = $2;
            $_ = $1.$3;
            if ($2 =~ /\/\*/) {
                print STDERR "Error in processing comment within interface file $prototype_file in line $origline";
            }
        }

        # Parse all routines returning an error code
        print "\nParsing : $_" if $gDebug;
        if (/^int\s+$routine_prefix($routine_pattern)\s*\((.*)/) {
            $routine_name = $1;
            $args = $2;
            while (! ($args =~ /;/)) {
                $args .= <FD>;
            }
            $args =~ s/MPICH_ATTR[A-Z_]*\([^)]*\)//g;
            $args =~ s/MPICH_API_PUBLIC//g;
            $args =~ s/\)\s*;//g;
            $args =~ s/[\r\n]*//g;
            # remove qualifiers from args
            $args =~ s/\s*const\s+//g;

            print "Found   : $routine_name($args)\n" if $gDebug;
            # Eventually, we'll create a new file here.
            # For C++, we may create similar files by looking up
            # the corresponding routines.
            # Check for duplicates in the list of routines
            if (defined($$routine_hash{$routine_name})) {
                print STDERR "Duplicate prototypes for $routine_name\n";
                next;
            }
            # Separate argument types and names
            my ($argtypes, $argnames) = &separate_args_and_append_ierror($args);
            # # Handle special cases
            # my $testname = $routine_name . "_args";
            # if (defined($$testname)) {
            # print "replacing args for $routine_name\n" if $gDebug;
            # $args = $$testname;
            # }
            $$routine_hash{$routine_name} = [$argtypes, $argnames];
        }
    }
}

#
# Look through $args, separate type and name of each argument. Then group argument
# types and names separately. In addition, append "int", "ierror" to existing
# argument types and names respectively.
# Input: an argument string
# Output: ($argtypes, $argnames), in which types (names) are delimited by comma.
sub separate_args_and_append_ierror {
    my $argtypes = "";
    my $argnames = "";
    my $comma = ""; # no comma before the first arg
    for my $parm (split(',', $_[0])) {
        my $argtype = "";
        my $argname = "";
        # Remove any leading or trailing spaces
        $parm =~ s/^\s*//;
        $parm =~ s/\s*$//;
        # Remove and remember qualifiers
        $qualifier = "";
        if ($parm =~ /^const\s+(.*)/) {
            $qualifier = "const ";
            $parm = $1;
        }

        if ($parm eq "void") { # e.g., MPI_Finalize(void)
            last;
        }

        # Question: What to do with the qualifier?
        # Handle parameters with parameter names
        # 1. "int foo"
        # 2. "int *foo"
        # 3. "int foo[]"
        # 4. "int *foo[]" or "int **foo[]"
        if ( ($parm =~ /^([A-Za-z0-9_]+)\s+([A-Za-z0-9_]+)$/) ) {
            $argtype = $1;
            $argname = $2;
        }
        elsif ( ($parm =~ /^([A-Za-z0-9_]+\s*\*)\s*([A-Za-z0-9_]+)$/) ) {
            $argtype = $1;
            $argname = $2;
        }
        elsif ( ($parm =~ /^([A-Za-z0-9_]+)\s*([A-Za-z0-9_]+)(\[.*\])\s*$/) ) {
            my $basetype = $1;
            my $arraytype = $3;
            #if ($arraytype =~ /\[\s*\]/) { $arraytype = "*"; }
            $argtype = $basetype . $arraytype;
            $argname = $2;
        }
        elsif ( ($parm =~ /^([A-Za-z0-9_]+)\s(\*?\*?)\s*([A-Za-z0-9_]+)(\[.*\])\s*$/) ) {
            my $basetype = $1;
            my $arraytype = $2 . $4;
            #if ($arraytype =~ /\[\s*\]/) { $arraytype = "*"; }
            $argtype = $basetype . $arraytype;
            $argname = $3;
        }

        $argtypes .= "$comma$argtype";
        $argnames .= "$comma$argname";
        $comma = ",";
    }

    # Append "int ierror"
    $argtypes .= "${comma}int";
    $argnames .= "${comma}ierror";
    print "argtypes= $argtypes\n" if $gDebug;
    print "argnames= $argnames\n" if $gDebug;
    return ($argtypes, $argnames);
}

# Since this is a required package, indicate that we are successful.
return 1;
